﻿namespace Mydra.IsdbTools
{
    using System.Collections.Generic;
    using System.IO;
    using System.Net;

    public class IsdbProtocol
    {
        /// <summary>
        /// The backing field for the <see cref="UserAgent"/> property.
        /// </summary>
        private static string _userAgent = "Media Player Classic";

        // FIXME : Make it configurable through the SettingsManager !

        /// <summary>
        /// The mask for the Http query to the ISDb server to query the matching subtitles.
        /// </summary>
        /// <remarks>Pattern : {0} = filename; {1} = filesize (hex); {2} = hash</remarks>
        private static string _isdbQueryMask = @"http://www.opensubtitles.org/isdb/index.php?player=mpc&name[0]={0}&size[0]={1}&hash[0]={2}";

        /// <summary>
        /// The mask for the Http query to the ISDb server to download the specified subtitles file.
        /// </summary>
        /// <remarks>Pattern : {0} = subtitle ID; {1} = query-ticket</remarks>
        private static string _isdbDownloadMask = @"http://www.opensubtitles.org/isdb/dl.php?id={0}&ticket={1}";

        /// <summary>
        /// The movie factory implementation to use when instanciating a new ISDb Movie.
        /// </summary>
        /// <remarks>By default, this member is set to an instance of type <see cref="IsdbMovieFactory"/>.</remarks>
        private static IIsdbMovieFactory _isdbMovieFactory = new IsdbMovieFactory();

        /// <summary>
        /// The subtitles factory implementation to use when instanciating a new ISDb subtitle file.
        /// </summary>
        /// <remarks>By default, this member is set to an instance of type <see cref="IsdbSubtitleFactory"/>.</remarks>
        private static IIsdbSubtitleFactory _isdbSubtitleFactory = new IsdbSubtitleFactory();

        /// <summary>
        /// Sets the isdb movie factory.
        /// </summary>
        /// <param name="movieFactory">The movie factory.</param>
        /// <remarks>By default, this property is set to an instance of type <see cref="IsdbMovieFactory"/>.</remarks>
        public static void SetIsdbMovieFactory(IIsdbMovieFactory movieFactory)
        {
            _isdbMovieFactory = movieFactory;
        }

        /// <summary>
        /// Sets the isdb subtitle factory.
        /// </summary>
        /// <param name="subtitleFactory">The subtitle factory.</param>
        /// <remarks>By default, this property is set to an instance of type <see cref="IsdbSubtitleFactory"/>.</remarks>
        public static void SetIsdbSubtitleFactory(IIsdbSubtitleFactory subtitleFactory)
        {
            _isdbSubtitleFactory = subtitleFactory;
        }

        /// <summary>
        /// Gets the subtitles per movie.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="fileSize">Size of the file.</param>
        /// <param name="hash">The movie file ISDb hash.</param>
        /// <param name="ticket">The query ticket to use for the actual subtitle download.</param>
        /// <returns>
        /// A list of the matching movies found with their related subtitles.
        /// </returns>
        public static IEnumerable<IIsdbMovie> GetSubtitlesPerMovie(string fileName, long fileSize, string hash, out string ticket)
        {
            string hexFormatFileSize = string.Format("{0:X}", fileSize);
            var isdbResponse = RequestIsdbSubtitles(fileName, hexFormatFileSize, hash);
            return ParseIsdbResponse(isdbResponse, out ticket);
        }

        /// <summary>
        /// Parses the isdb response.
        /// </summary>
        /// <param name="isdbResponse">The isdb response.</param>
        /// <param name="s"></param>
        /// <remarks>Based on the MPC implementation by Gabest, see http://www.koders.com/cpp/fidC4C5948D3515DCFE045B8E67917F86AD890D0B8F.aspx lines 3996 and following.</remarks>
        /// <returns></returns>
        public static IEnumerable<IIsdbMovie> ParseIsdbResponse(string isdbResponse, out string ticket)
        {
            ticket = null;
            var movies = new List<IIsdbMovie>();
            IIsdbMovie movie = null;
            using (var reader = new StringReader(isdbResponse))
            {
                string line;
                IIsdbSubtitle subtitle = null;
                while ((line = reader.ReadLine()) != null)
                {
                    var keyAndValue = line.Split('=');
                    var param = keyAndValue[0];
                    var value = string.Empty;

                    if (keyAndValue.Length > 1)
                    {
                        value = keyAndValue[1];
                    }

                    switch (param)
                    {
                        case "ticket":
                            ticket = value;
                            break;
                        case "movie":
                            movie = _isdbMovieFactory.Create();
                            movie.Titles = value.Split('|');
                            break;
                        case "subtitle":
                            subtitle = _isdbSubtitleFactory.Create();
                            subtitle.ID = int.Parse(value);
                            break;
                        case "name":
                            subtitle.Name = value;
                            break;
                        case "discs":
                            subtitle.Discs = int.Parse(value);
                            break;
                        case "disc_no":
                            subtitle.DiscNo = int.Parse(value);
                            break;
                        case "format":
                            subtitle.Format = value;
                            break;
                        case "iso639_2":
                            subtitle.Iso6392 = value;
                            break;
                        case "language":
                            subtitle.Language = value;
                            break;
                        case "nick":
                            subtitle.Nick = value;
                            break;
                        case "email":
                            subtitle.Email = value;
                            break;
                        case "endsubtitle":
                            movie.Subtitles.Add(subtitle);
                            break;
                        case "endmovie":
                            movies.Add(movie);
                            break;
                        case "end":
                            return movies;
                        default:
                            break;
                    }
                }
            }


            return movies;
        }

        /// <summary>
        /// Requests the isdb subtitles.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="hexFormatFileSize">Size of the hex format file.</param>
        /// <param name="movieHash">The movie file ISDb hash.</param>
        /// <returns>The ISDb server response in the native ISDb format.</returns>
        public static string RequestIsdbSubtitles(string fileName, string hexFormatFileSize, string movieHash)
        {
            var client = new WebClient();
            client.Headers.Add("User-Agent", UserAgent);
            var queryString = string.Format(
                _isdbQueryMask,
                fileName,
                hexFormatFileSize,
                movieHash);

            var isdbResponse = client.DownloadString(queryString);
            
            return isdbResponse;
        }

        /// <summary>
        /// Downloads the ISDb subtitle.
        /// </summary>
        /// <param name="subtitleID">The subtitle ID.</param>
        /// <param name="ticket">The query ticket to use for the actual subtitle download.</param>
        /// <returns>A stream to the content of the subtitle.</returns>
        public static Stream DownloadIsdbSubtitle(int subtitleID, string ticket)
        {
            var client = new WebClient();
            client.Headers.Add("User-Agent", UserAgent);
            var queryString = string.Format(
                _isdbDownloadMask,
                subtitleID,
                ticket);

            var isdbResponse = client.OpenRead(queryString);

            return isdbResponse;
        }

        /// <summary>
        /// Gets or sets the user agent used in the http headers to the ISDb server.
        /// </summary>
        /// <value>The user agent.</value>
        public static string UserAgent
        {
            get
            {
                return _userAgent;
            }

            set
            {
                _userAgent = value;
            }
        }
    }
}